Skip to main content

libobs_simple\sources\windows\sources/
window_capture.rs

1use crate::{
2    define_object_manager,
3    sources::{
4        macro_helper::impl_custom_source,
5        windows::{ObsHookableSourceSignals, ObsHookableSourceTrait},
6    },
7};
8
9use super::{ObsWindowCaptureMethod, ObsWindowPriority};
10use crate::error::ObsSimpleError;
11use libobs_simple_macro::obs_object_impl;
12#[cfg(feature = "window-list")]
13use libobs_window_helper::{get_all_windows, WindowInfo, WindowSearchMode};
14use libobs_wrapper::{
15    data::{ObsObjectBuilder, ObsObjectUpdater},
16    scenes::{ObsSceneItemRef, ObsSceneRef, SceneItemExtSceneTrait},
17    sources::{ObsSourceBuilder, ObsSourceRef},
18    utils::ObsError,
19};
20use num_traits::ToPrimitive;
21
22define_object_manager!(
23    /// Provides an easy-to-use builder for the window capture source.
24    #[derive(Debug)]
25    struct WindowCaptureSource("window_capture", *mut libobs::obs_source) for ObsSourceRef {
26
27    /// Sets the priority of the window capture source.
28    /// Used to determine in which order windows are searched for.
29    ///
30    /// # Arguments
31    ///
32    /// * `priority` - The priority of the window capture source.
33    ///
34    /// # Returns
35    ///
36    /// The updated `WindowCaptureSourceBuilder` instance.
37    #[obs_property(type_t = "enum")]
38    priority: ObsWindowPriority,
39
40    /// Sets the window to capture.
41    ///
42    /// # Arguments
43    ///
44    /// * `window` - The window to capture, represented as `ObsString`. Must be in the format of an obs window id
45    ///
46    /// # Returns
47    ///
48    /// The updated `WindowCaptureSourceBuilder` instance.
49    #[obs_property(type_t = "string", settings_key = "window")]
50    window_raw: String,
51
52    #[obs_property(type_t = "bool")]
53    /// Sets whether the cursor should be captured
54    cursor: bool,
55
56    /// Whether to capture audio from window source (BETA) <br>
57    /// When enabled, creates an "Application Audio Capture" source that automatically updates to the currently captured window/application. <br>
58    /// Note that if Desktop Audio is configured, this could result in doubled audio.
59    capture_audio: bool,
60
61    #[obs_property(type_t = "bool")]
62    /// Whether to force SDR color space for the window capture source.
63    force_sdr: bool,
64
65    #[obs_property(type_t = "bool")]
66    /// Whether to capture the window's client area only (without borders, title bar and the main menu bar).
67    client_area: bool,
68
69    #[obs_property(type_t = "bool")]
70    compatibility: bool,
71
72    capture_method: Option<ObsWindowCaptureMethod>,
73});
74
75#[cfg(feature = "window-list")]
76#[libobs_simple_macro::obs_object_impl]
77impl WindowCaptureSource {
78    /// Gets a list of windows that can be captured by this source.
79    pub fn get_windows(
80        mode: WindowSearchMode,
81    ) -> Result<Vec<libobs_wrapper::unsafe_send::Sendable<WindowInfo>>, ObsSimpleError> {
82        Ok(get_all_windows(mode)
83            .map_err(ObsSimpleError::WindowHelperError)?
84            .into_iter()
85            .map(libobs_wrapper::unsafe_send::Sendable)
86            .collect())
87    }
88
89    /// Sets the window to capture.
90    ///
91    /// # Arguments
92    ///
93    /// * `window` - The window to capture. A list of available windows can be retrieved using `WindowCaptureSourceBuilder::get_windows`
94    ///
95    /// # Returns
96    ///
97    /// The updated `WindowCaptureSourceBuilder` instance.
98    pub fn set_window(self, window: &libobs_wrapper::unsafe_send::Sendable<WindowInfo>) -> Self {
99        self.set_window_raw(window.0.obs_id.as_str())
100    }
101}
102
103impl<'a> WindowCaptureSourceUpdater<'a> {
104    pub fn set_capture_method(mut self, method: ObsWindowCaptureMethod) -> Self {
105        self.get_settings_updater()
106            .set_int_ref("method", method.to_i32().unwrap() as i64);
107
108        self
109    }
110}
111
112impl WindowCaptureSourceBuilder {
113    /// Sets the capture method for the window capture source.
114    pub fn set_capture_method(mut self, method: ObsWindowCaptureMethod) -> Self {
115        self.capture_method = Some(method);
116        self
117    }
118}
119
120#[obs_object_impl]
121impl WindowCaptureSource {
122    pub fn set_capture_audio(mut self, capture_audio: bool) -> Result<Self, ObsSimpleError> {
123        use crate::sources::windows::audio_capture_available;
124
125        if capture_audio && !audio_capture_available(self.runtime())? {
126            return Err(ObsSimpleError::FeatureNotAvailable(
127                "Game Audio Capture is not available on this system",
128            ));
129        }
130
131        self.get_settings_updater()
132            .set_bool_ref("capture_audio", capture_audio);
133
134        Ok(self)
135    }
136}
137
138impl_custom_source!(
139    WindowCaptureSource,
140    ObsHookableSourceSignals,
141    NO_SPECIFIC_SIGNALS_FUNCTION
142);
143
144impl ObsHookableSourceTrait for WindowCaptureSource {
145    fn source_specific_signals(&self) -> std::sync::Arc<ObsHookableSourceSignals> {
146        self.source_specific_signals.clone()
147    }
148}
149
150impl ObsSourceBuilder for WindowCaptureSourceBuilder {
151    type T = WindowCaptureSource;
152
153    fn build(self) -> Result<Self::T, ObsError> {
154        let runtime = self.runtime.clone();
155
156        let b = self.object_build()?;
157
158        let source = ObsSourceRef::new_from_info(b, runtime)?;
159        WindowCaptureSource::new(source)
160    }
161
162    fn add_to_scene(mut self, scene: &mut ObsSceneRef) -> Result<ObsSceneItemRef<Self::T>, ObsError>
163    where
164        Self: Sized,
165    {
166        // Because of a black screen bug, we need to set the method to WGC first and then update (I've copied this code from the DisplayCapture source, they should have the same issue)
167        self.get_settings_updater().set_int_ref(
168            "method",
169            ObsWindowCaptureMethod::MethodAuto.to_i32().unwrap() as i64,
170        );
171
172        let method_to_set = self.capture_method;
173        let mut source = self.build()?;
174        let scene_item = scene.add_source(source.clone())?;
175
176        if let Some(method) = method_to_set {
177            source
178                .create_updater()?
179                .set_capture_method(method)
180                .update()?;
181        }
182
183        Ok(scene_item)
184    }
185}